package com.young.config;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPoolConfig;

/**
 * @Description: Shiro的配置类
 * @Author: kangyang
 * @Date: 2021/5/8 9:55
 **/
@Configuration
public class ShiroConfig {

    @Value("${shiro.redis}")
    private String redisHost;

    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(UserRealm realm, TokenSessionMannager sessionManager, HashedCredentialsMatcher credentialsMatcher) {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        // realm设置密码验证器
        realm.setCredentialsMatcher(credentialsMatcher);
        // 设置realm
        defaultWebSecurityManager.setRealm(realm);
        // 设置sessionMananger
        defaultWebSecurityManager.setSessionManager(sessionManager);
        return defaultWebSecurityManager;
    }

    /**
     * @Author kangyang
     * @Description 自定义token
     * @Date 14:33 2021/5/8
     * @Param []
     * @return com.young.config.TokenSessionMannager
     **/
    @Bean
    public TokenSessionMannager tokenSessionMannager(RedisSessionDAO sessionDAO) {
        TokenSessionMannager tokenSessionMannager = new TokenSessionMannager();
        tokenSessionMannager.setSessionDAO(sessionDAO);
        return tokenSessionMannager;
    }

    /**
     * @Author kangyang
     * @Description 把token放到redis中
     * @Date 17:35 2021/5/8
     * @Param [redisManager]
     * @return org.crazycake.shiro.RedisSessionDAO
     **/
    @Bean
    public RedisSessionDAO redisSessionDAO(RedisManager redisManager) {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager);
        return redisSessionDAO;
    }

    @Bean
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(5);
        jedisPoolConfig.setMaxIdle(3);
        jedisPoolConfig.setMinIdle(2);
        redisManager.setJedisPoolConfig(jedisPoolConfig);
        redisManager.setHost(redisHost);
        return redisManager;
    }

    /**
     * @Author kangyang
     * @Description 放行和拦截
     * @Date 17:39 2021/5/8
     * @Param []
     * @return org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition
     **/
    @Bean
    public DefaultShiroFilterChainDefinition defaultShiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition defaultShiroFilterChainDefinition = new DefaultShiroFilterChainDefinition();
        defaultShiroFilterChainDefinition.addPathDefinition("/login", "anon");
        defaultShiroFilterChainDefinition.addPathDefinition("/captcha.jpg", "anon");
        defaultShiroFilterChainDefinition.addPathDefinition("/**", "authc");
        // 放开提供测试
//        defaultShiroFilterChainDefinition.addPathDefinition("/**", "anon");

        return defaultShiroFilterChainDefinition;
    }

    /**
     * @Author kangyang
     * @Description 注解的权限验证
     * @Date 17:41 2021/5/8
     * @Param [defaultWebSecurityManager]
     * @return org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor
     **/
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager defaultWebSecurityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * @Author kangyang
     * @Description 密码匹配器
     * @Date 17:43 2021/5/8
     * @Param []
     * @return org.apache.shiro.authc.credential.HashedCredentialsMatcher
     **/
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher("MD5");
        hashedCredentialsMatcher.setHashIterations(2);
        return hashedCredentialsMatcher;
    }

    /**
     * 在spring 里面的反射生成Controller 里面使用Cglib
     * 但是使用CGLIB 生成的对象，他有接口
     * xxx iml Enhancer
     * shiro 再次生成代理对象时，发现Controller 都有接口，有接口使用JDK（Cglib） 生成，导致出错了
     * 由于使用了aop:aspectj-autoproxy强制了proxy-target-class
     也就是说对Web层的Class(主要是Controller)使用了CGLib代理
     然后在Shiro进行代理时使用DefaultAdvisorAutoProxyCreator
     原本应该判断Controller，发现没有任何接口，所以使用CGLib来代理
     但是由于Controller已经被CGLib代理过一次了
     DefaultAdvisorAutoProxyCreator拿到对不是Contoller本身，而是CGLib的代理结果
     CGLib的代理结果本身是有接口的，干扰了DefaultAdvisorAutoProxyCreator的内部判断
     使用JDK去代理CGLib的代理结果
     结果Controller的函数时去了CGLib的接口中找方法名，发现方法不存在，导致代理失败
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
        daap.setProxyTargetClass(true); // 使用cglib 来生成代理类，默认使用jdk 生成 默认为false
        //		proxy-target-class true：cglib  false：jdk
        // 事务管理器 cglib true
        return daap;
    }

}
